#include "networkdevice.h"

#include <string.h>
#include "error.h"

NetworkDevice::NetworkDevice()
{
    running = false;
	driver = 0;
}

NetworkDevice::~NetworkDevice()
{
    if( running )
        cleanup();
}


int NetworkDevice::init()
{
    net_init();
    net_loadconfig(NULL);
    net_detectdrivers(net_drivers_all);
    net_initdrivers(net_drivers_all);

    running = true;

    return true;
}

int NetworkDevice::cleanup()
{
    net_shutdown();
    return true;
}

void NetworkDevice::pushKey(std::string nn, std::string nk)
{
    KeyPair tmp;
    tmp.name = nn;
    tmp.key = nk;
    keyQueue.push_back(tmp);
}

void NetworkDevice::registerKeys()
{
    AES newaes;
    newaes.setkey(DEFAULT_KEY);
    aesMap["default"] = newaes;

    for(unsigned int i = 0; i < keyQueue.size(); i++)
    {
        AES newaes;
        newaes.setkey(keyQueue[i].key);

        aesMap[keyQueue[i].name] = newaes;
    }

    keyQueue.clear();
}

void NetworkDevice::flushKeys()
{
    keyQueue.clear();
    aesMap.clear();
}

NetCon NetworkDevice::openCon(const char *binding, std::string keyname)
{
	NET_DRIVERLIST drivers;
	NET_DRIVERNAME *drivernames;
	drivers = net_detectdrivers(net_classes[NET_CLASS_INET].drivers);
	drivernames = net_getdrivernames(drivers);

	// Assume driver 0 (default)
	driver = drivernames[0].num;

    NetCon newcon;
    newcon.conn = net_openconn(driver, binding);
    newcon.setkeyname(keyname.c_str());

    return newcon;
}

int NetworkDevice::listen(const NetCon &netcon)
{
    return net_listen(netcon.conn);
}

NetCon NetworkDevice::pollListen(const NetCon &netcon)
{
    NetCon newcon;
    newcon.conn = net_poll_listen(netcon.conn);
    newcon.setkeyname(netcon.keyname);

    return newcon;
}

int NetworkDevice::connect(const NetCon &netcon, const char *addr)
{
    return net_connect(netcon.conn, addr);
}

int NetworkDevice::connectStatus(const NetCon &netcon)
{
    return net_poll_connect(netcon.conn);
}

int NetworkDevice::pollMessages(const NetCon &netcon)
{
	//printf("[DEBUG] pollMessage\n");
    int status = net_query_rdm(netcon.conn);
    return status;
}

std::string NetworkDevice::getMessage(const NetCon &netcon)
{
	//printf("[DEBUG] getMessage\n");
    unsigned int msgsize = net_query_rdm(netcon.conn);
    char *buffer = new char[msgsize+1];
    char *decrypted = new char[msgsize+1];

	if( buffer == NULL )
		allocationError();
	if( decrypted == NULL )
		allocationError();

    /*int bytesread = */net_receive_rdm(netcon.conn, (void*)buffer, msgsize);

    // decrypt the message
    t_aesMap::iterator foundpos = aesMap.find(netcon.keyname);

    unsigned long dlen;

    if( foundpos == aesMap.end() )
        dlen = aesMap["default"].decrypt(buffer, decrypted, msgsize, msgsize);
    else
        dlen = aesMap[netcon.keyname].decrypt(buffer, decrypted, msgsize,
            msgsize);

    std::string retstring;
    retstring.assign(decrypted, dlen);

    delete [] buffer;
    delete [] decrypted;

    return retstring;
}

int NetworkDevice::sendMessage(const NetCon &netcon, const char *buff, int size)
{
	//printf("[DEBUG] sendMessage\n");
    char *encrypted = new char[size+17]; // account for blocksize if necessary
	if( encrypted == NULL )
		allocationError();

    t_aesMap::iterator foundpos = aesMap.find(netcon.keyname);
    unsigned long elen;

    if( foundpos == aesMap.end() )
        elen = aesMap["default"].encrypt(buff, encrypted, size, size+16);
    else
        elen = aesMap[netcon.keyname].encrypt(buff, encrypted, size, size+16);


    int failstate = net_send_rdm(netcon.conn, (void*)encrypted, elen);

    delete [] encrypted;

    return failstate;
}

char *NetworkDevice::getAddress(const NetCon &netcon)
{
	//printf("[DEBUG] getAddress\n");
	return net_getpeer(netcon.conn);
}

void NetworkDevice::closeCon(NetCon &netcon)
{
	//printf("[DEBUG] closeCon\n");
	net_closeconn(netcon.conn);
	netcon.conn = 0;
}


/* NetCon */
void NetCon::setkeyname(const char *newname)
{
    strcpy((char*)&keyname, newname);
}
